#include "n3.h"

enum {
	N3_FAIL = 0,
	N3_WAIT_S,
	N3_WAIT_P,
	N3_WAIT_O,
	N3_EOS,
	N3_EOF
};

enum {
	T_INIT = 0,
	T_SYM,
	T_STR,
	T_SYMESC,
	T_STRESC,
	T_END
};

int n3_read_term(FILE* f, char* buf, size_t len) {
	int state = T_INIT;
	int c = fgetc(f);
	char* end = buf + len;
	while ( c != EOF && state != T_END && buf < end  ) {
		//fprintf(stderr, "T_STATE=%d\n", state);
		switch(state) {

		case T_INIT:
			switch ( c ) {
			case '\"':
				state = T_STR;
				break;
			case ' ':
			case '\t':
			case '\n':
			case '\r':
				state = T_INIT;
				break;
			default:
				state = T_SYM;
				*buf = c;
				buf++;
				break;
			}
			break;
		case T_SYM:
			switch ( c ) {
			case ' ':
			case '\t':
			case '\r':
			case '\n':
				*buf = 0;
				state = T_END;
				break;
			case '\\':
				state = T_SYMESC;
				break;
			default:
				*buf = c;
				buf++;
				break;
			}
			break;
		case T_SYMESC:
			switch ( c ) {
			case 't':
				*buf = '\t';
				break;
			case 'n':
				*buf = '\n';
				break;
			case 'r':
				*buf = '\r';
				break;
			default:
				*buf = c;
				break;
			}
			buf++;
			state = T_SYM;
			break;
		case T_STR:
			switch (c) {
			case '\"':
				state = T_END;
				*buf = 0;
				break;
			case '\\':
				state = T_STRESC;
				break;
			default:
				*buf = c;
				buf++;
				break;
			}
			break;
		case T_STRESC:
			switch ( c ) {
			case 't':
				*buf = '\t';
				break;
			case 'n':
				*buf = '\n';
				break;
			case 'r':
				*buf = '\r';
				break;
			default:
				*buf = c;
				break;
			}
			buf++;
			state = T_STR;
			break;
		default:
			fprintf(stderr, "N3 Parser: unknown state %d\n", state);
			return 0;
		}
		c = fgetc(f);
	}
	if ( buf == end ) {
		fprintf(stderr, "Buffer of length %d is not sufficient for reading term\n", len);
		return 0;
	}
	if ( c == EOF ) 
		return 2;
	if ( state != T_END ) {
		fprintf(stderr, "Incomplete term read\n");
		return 0;
	}
	ungetc(c, f);		
	//fprintf(stderr, "Term read: <%s>\n", end - len);
	return 1;
}

int n3_read(FILE* f, Statement** list) {
	int state = N3_WAIT_S;
	int c;
	char s[1024];
	char p[1024];
	char o[1024];
	while ( state != N3_FAIL && state != N3_EOF ) {
		//printf("state: %d\n", state);
		switch(state) {
		case N3_WAIT_S:
			c = n3_read_term(f, s, sizeof(s));
			if (c == 2 )
				state = N3_EOF;
			else if ( !c ) {
				state = N3_FAIL;
			} else {
				state = N3_WAIT_P;
			}
			break;
		case N3_WAIT_P:
			if ( ! n3_read_term(f, p, sizeof(p)) ) {
				state = N3_FAIL;
			} else {
				state = N3_WAIT_O;
			}
			break;
		case N3_WAIT_O:
			if ( ! n3_read_term(f, o, sizeof(o)) ) {
				state = N3_FAIL;
			} else {
				Statement* new = stmt_new(s, p, o, 1);
				if ( ! new ) {
					fprintf(stderr, "N3: Cannot create new statement\n");
					return 0;
				}
				stmt_prepend(list, new);
				state = N3_EOS;
			}
			break;
		case N3_EOS: {
			int c = fgetc(f);
			//fprintf(stderr, "Read in EOS: <%c>\n", c);
			switch(c) {
				case ' ':
				case '\t':
				case '\n':
				case '\r':
					break;
				case ',':
					state = N3_WAIT_O;
					break;
				case ';':
					state = N3_WAIT_P;
					break;
				case '.':
					//fprintf(stderr, " Dot read\n");
					state = N3_WAIT_S;
					break;
				case EOF:
					state = N3_EOF;
					break;
				default:
					state = N3_FAIL;
					break;
			}
			break;
			}
		default:
			fprintf(stderr, "N3: Unknown state %d\n", state);
			return 0;
		}
	}
	if ( state != N3_EOF) {
		fprintf(stderr, "N3: incomplete state\n");
		return 0;
	}
	return 1;
}


int need_string(const char* str) {
	while (*str) {
		if( *str == ' ' || *str == '\\' ||
				*str == '\"' || *str == '\t' || *str == '\n' || *str == '\r')
			return 1;
		str++;
	}
	return 0;

}

int n3_write_escaped(FILE* f, const char* str) {
	const char* s = str;
	while(*s) {
		if (*s == '\t') 
			fputs("\\t", f);
		else if (*s == '\n' )
			fputs("\\n", f);
		else if (*s == '\r' )
			fputs("\\r", f);
		else if (*s == '\"' )
			fputs("\\\"", f);
		else if (*s == '\\' )
			fputs("\\\\", f);
		else 
			fputc(*s, f);
		s++;
	}

}

int n3_write_sym(FILE* f, const char* sym) {
	if ( need_string(sym) ) {
		fputc('\"', f);
		n3_write_escaped(f, sym);
		fputc('\"', f);
	} else {
		n3_write_escaped(f, sym);
	}
	fputc(' ', f);
}

int n3_write_statement(FILE* f, Statement* stmt) {
	if (! n3_write_sym(f, stmt->s) )
		return 0;
	if ( !n3_write_sym(f, stmt->p) )
		return 0;
	if ( !n3_write_sym(f, stmt->o) )
		return 0;

	fprintf(f, " .\n");
	return 1;
}


int n3_write(FILE* f, Statement* list) {
	Statement* cur = list;
	while (cur) {
		if (! n3_write_statement(f, cur)) {
			return 0;
		}
		cur = list->next;
		
	}
	fflush(f);
	return 1;
}



